package service

import (
	"context"
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"io"
	"net/http"
	"sync"
	"time"

	"github.com/gorilla/websocket"
	"github.com/sirupsen/logrus"
	"libvirt.org/go/libvirt"
)

// SerialProxy 串行控制台代理
type SerialProxy struct {
	connMgr  *ConnectionManager
	logger   *logrus.Logger
	upgrader websocket.Upgrader

	// 会话管理
	sessions   map[string]*serialSession
	sessionsMu sync.RWMutex

	// 令牌管理
	tokens   map[string]*consoleToken
	tokensMu sync.RWMutex
}

// serialSession 串行控制台会话
type serialSession struct {
	id         string
	connID     string
	vmName     string
	stream     *libvirt.Stream
	ws         *websocket.Conn
	ctx        context.Context
	cancel     context.CancelFunc
	logger     *logrus.Logger
	startTime  time.Time
	lastActive time.Time
	bytesRead  uint64
	bytesWrite uint64

	// 控制通道
	pingTicker *time.Ticker
	stopCh     chan struct{}
}

// consoleToken 控制台访问令牌
type consoleToken struct {
	token       string
	connID      string
	vmName      string
	consoleType string // serial
	createdAt   time.Time
	expiresAt   time.Time
}

// SerialInfo 串行控制台信息
type SerialInfo struct {
	ProxyURL  string    `json:"proxy_url"`
	WSPath    string    `json:"ws_path"`
	Token     string    `json:"token"`
	ExpiresAt time.Time `json:"expires_at"`
	VMName    string    `json:"vm_name"`
	State     string    `json:"state"`
}

// SerialSessionInfo 会话信息
type SerialSessionInfo struct {
	ID         string    `json:"id"`
	VMName     string    `json:"vm_name"`
	StartTime  time.Time `json:"start_time"`
	LastActive time.Time `json:"last_active"`
	BytesRead  uint64    `json:"bytes_read"`
	BytesWrite uint64    `json:"bytes_write"`
	Duration   string    `json:"duration"`
}

func NewSerialProxy(connMgr *ConnectionManager, logger *logrus.Logger) *SerialProxy {
	return &SerialProxy{
		connMgr: connMgr,
		logger:  logger,
		upgrader: websocket.Upgrader{
			ReadBufferSize:  4096,
			WriteBufferSize: 4096,
			CheckOrigin: func(r *http.Request) bool {
				// 生产环境应验证 Origin
				return true
			},
		},
		sessions: make(map[string]*serialSession),
		tokens:   make(map[string]*consoleToken),
	}
}

// GetSerialInfo 获取串行控制台连接信息
func (p *SerialProxy) GetSerialInfo(ctx context.Context, connID, vmName string) (*SerialInfo, error) {
	p.logger.WithFields(logrus.Fields{
		"connection": connID,
		"vm":         vmName,
	}).Debug("Getting serial console info")

	connection, err := p.connMgr.GetConnection(connID)
	if err != nil {
		return nil, fmt.Errorf("failed to get connection: %w", err)
	}

	// 查找虚拟机
	dom, err := connection.conn.LookupDomainByName(vmName)
	if err != nil {
		return nil, fmt.Errorf("VM not found: %w", err)
	}
	defer func() {
		if err := dom.Free(); err != nil {
			p.logger.WithError(err).Warn("Failed to free domain")
		}
	}()

	// 检查虚拟机状态
	state, _, err := dom.GetState()
	if err != nil {
		return nil, fmt.Errorf("failed to get VM state: %w", err)
	}

	vmState := "shutoff"
	if state == libvirt.DOMAIN_RUNNING {
		vmState = "running"
	}

	// 生成访问令牌
	token := p.generateToken(connID, vmName, "serial")

	info := &SerialInfo{
		ProxyURL:  fmt.Sprintf("console://%s/%s", connID, vmName),
		WSPath:    fmt.Sprintf("/api/v1/console/serial?connection=%s&name=%s&token=%s", connID, vmName, token),
		Token:     token,
		ExpiresAt: time.Now().Add(1 * time.Hour),
		VMName:    vmName,
		State:     vmState,
	}

	p.logger.WithFields(logrus.Fields{
		"vm":         vmName,
		"token":      token[:8] + "...",
		"expires_at": info.ExpiresAt,
	}).Info("Serial console info generated")

	return info, nil
}

// HandleSerialWebSocket 处理串行控制台 WebSocket 连接
func (p *SerialProxy) HandleSerialWebSocket(w http.ResponseWriter, r *http.Request, connID, vmName string) error {
	// 验证令牌
	token := r.URL.Query().Get("token")
	if !p.validateToken(token, connID, vmName, "serial") {
		http.Error(w, "Invalid or expired token", http.StatusUnauthorized)
		return fmt.Errorf("invalid token")
	}

	// 升级到 WebSocket
	ws, err := p.upgrader.Upgrade(w, r, nil)
	if err != nil {
		return fmt.Errorf("failed to upgrade to websocket: %w", err)
	}

	p.logger.WithFields(logrus.Fields{
		"connection": connID,
		"vm":         vmName,
		"remote":     r.RemoteAddr,
	}).Info("Serial console WebSocket connection established")

	// 连接到 libvirt 串行控制台
	stream, err := p.openSerial(connID, vmName)
	if err != nil {
		ws.Close()
		return fmt.Errorf("failed to open serial console: %w", err)
	}

	// 创建会话
	ctx, cancel := context.WithCancel(r.Context())
	sessionID := p.generateSessionID(vmName)

	session := &serialSession{
		id:         sessionID,
		connID:     connID,
		vmName:     vmName,
		stream:     stream,
		ws:         ws,
		ctx:        ctx,
		cancel:     cancel,
		logger:     p.logger,
		startTime:  time.Now(),
		lastActive: time.Now(),
		pingTicker: time.NewTicker(30 * time.Second),
		stopCh:     make(chan struct{}),
	}

	// 注册会话
	p.sessionsMu.Lock()
	p.sessions[sessionID] = session
	p.sessionsMu.Unlock()

	// 清理会话
	defer func() {
		p.sessionsMu.Lock()
		delete(p.sessions, sessionID)
		p.sessionsMu.Unlock()

		session.cleanup()

		p.logger.WithFields(logrus.Fields{
			"session_id":  sessionID,
			"vm":          vmName,
			"duration":    time.Since(session.startTime),
			"bytes_read":  session.bytesRead,
			"bytes_write": session.bytesWrite,
		}).Info("Serial console session closed")
	}()

	// 发送欢迎消息
	if err := p.sendWelcomeMessage(session); err != nil {
		p.logger.WithError(err).Warn("Failed to send welcome message")
	}

	// 启动会话管理
	return p.runSession(session)
}

// openSerial 打开串行控制台
func (p *SerialProxy) openSerial(connID, vmName string) (*libvirt.Stream, error) {
	connection, err := p.connMgr.GetConnection(connID)
	if err != nil {
		return nil, err
	}

	dom, err := connection.conn.LookupDomainByName(vmName)
	if err != nil {
		return nil, fmt.Errorf("VM not found: %w", err)
	}
	defer func() {
		if err := dom.Free(); err != nil {
			p.logger.WithError(err).Warn("Failed to free domain")
		}
	}()

	// 检查虚拟机是否运行
	state, _, err := dom.GetState()
	if err != nil {
		return nil, fmt.Errorf("failed to get VM state: %w", err)
	}
	if state != libvirt.DOMAIN_RUNNING {
		return nil, fmt.Errorf("VM is not running (state: %d)", state)
	}

	// 创建 libvirt stream
	stream, err := connection.conn.NewStream(0)
	if err != nil {
		return nil, fmt.Errorf("failed to create stream: %w", err)
	}

	// 打开控制台（连接到第一个串行设备）
	devName := "serial0"
	flags := libvirt.DOMAIN_CONSOLE_FORCE | libvirt.DOMAIN_CONSOLE_SAFE

	if err := dom.OpenConsole(devName, stream, libvirt.DomainConsoleFlags(flags)); err != nil {
		if err := stream.Free(); err != nil {
			p.logger.WithError(err).Warn("Failed to free stream")
		}
		// 尝试使用空设备名（连接到默认控制台）
		stream, err = connection.conn.NewStream(0)
		if err != nil {
			return nil, fmt.Errorf("failed to create stream: %w", err)
		}

		if err := dom.OpenConsole("", stream, libvirt.DomainConsoleFlags(flags)); err != nil {
			if err := stream.Free(); err != nil {
				p.logger.WithError(err).Warn("Failed to free stream")
			}
			return nil, fmt.Errorf("failed to open console: %w", err)
		}
	}

	p.logger.WithField("vm", vmName).Info("Serial console stream opened")
	return stream, nil
}

// runSession 运行会话
func (p *SerialProxy) runSession(session *serialSession) error {
	errCh := make(chan error, 2)

	// WebSocket -> Serial Console
	go p.wsToConsole(session, errCh)

	// Serial Console -> WebSocket
	go p.consoleToWS(session, errCh)

	// Ping/Pong 保活
	go p.handlePingPong(session)

	// 等待错误或关闭
	select {
	case err := <-errCh:
		if err != nil && err != io.EOF {
			session.logger.WithError(err).Warn("Session error")
		}
		return err
	case <-session.ctx.Done():
		return session.ctx.Err()
	case <-session.stopCh:
		return nil
	}
}

// wsToConsole WebSocket 到控制台的数据转发
func (p *SerialProxy) wsToConsole(session *serialSession, errCh chan<- error) {
	defer session.cancel()

	for {
		select {
		case <-session.ctx.Done():
			return
		default:
		}

		// 设置读取超时
		if err := session.ws.SetReadDeadline(time.Now().Add(60 * time.Second)); err != nil {
			session.logger.WithError(err).Warn("Failed to set read deadline")
		}

		msgType, data, err := session.ws.ReadMessage()
		if err != nil {
			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseNormalClosure) {
				errCh <- fmt.Errorf("websocket read error: %w", err)
			}
			return
		}

		// 更新活跃时间
		session.lastActive = time.Now()

		// 处理 Pong 消息
		if msgType == websocket.PongMessage {
			continue
		}

		// 只处理文本和二进制消息
		if msgType != websocket.TextMessage && msgType != websocket.BinaryMessage {
			continue
		}

		// 写入控制台
		n, err := session.stream.Send(data)
		if err != nil {
			errCh <- fmt.Errorf("console write error: %w", err)
			return
		}

		session.bytesWrite += uint64(n)
		session.logger.WithFields(logrus.Fields{
			"session": session.id,
			"bytes":   n,
		}).Debug("Data sent to console")
	}
}

// consoleToWS 控制台到 WebSocket 的数据转发
func (p *SerialProxy) consoleToWS(session *serialSession, errCh chan<- error) {
	defer session.cancel()

	buffer := make([]byte, 4096)

	for {
		select {
		case <-session.ctx.Done():
			return
		default:
		}

		// 读取控制台输出
		n, err := session.stream.Recv(buffer)
		if err != nil {
			if err != io.EOF {
				// libvirt 特定错误处理
				if libvirtErr, ok := err.(libvirt.Error); ok {
					if libvirtErr.Code == libvirt.ERR_OK || libvirtErr.Code == libvirt.ERR_OPERATION_TIMEOUT {
						// 超时或正常，继续
						continue
					}
				}
				errCh <- fmt.Errorf("console read error: %w", err)
			}
			return
		}

		if n == 0 {
			continue
		}

		session.bytesRead += uint64(n)
		session.lastActive = time.Now()

		// 设置写入超时
		if err := session.ws.SetWriteDeadline(time.Now().Add(10 * time.Second)); err != nil {
			session.logger.WithError(err).Warn("Failed to set write deadline")
		}

		// 发送到 WebSocket（使用二进制消息）
		if err := session.ws.WriteMessage(websocket.BinaryMessage, buffer[:n]); err != nil {
			errCh <- fmt.Errorf("websocket write error: %w", err)
			return
		}

		session.logger.WithFields(logrus.Fields{
			"session": session.id,
			"bytes":   n,
		}).Debug("Data sent to websocket")
	}
}

// handlePingPong 处理 Ping/Pong 保活
func (p *SerialProxy) handlePingPong(session *serialSession) {
	defer session.pingTicker.Stop()

	for {
		select {
		case <-session.ctx.Done():
			return
		case <-session.pingTicker.C:
			if err := session.ws.SetWriteDeadline(time.Now().Add(10 * time.Second)); err != nil {
				session.logger.WithError(err).Warn("Failed to set write deadline")
			}
			if err := session.ws.WriteMessage(websocket.PingMessage, nil); err != nil {
				session.logger.WithError(err).Warn("Failed to send ping")
				session.cancel()
				return
			}

			// 检查空闲超时（10 分钟无活动）
			if time.Since(session.lastActive) > 10*time.Minute {
				session.logger.WithField("session", session.id).Info("Session idle timeout")
				session.cancel()
				return
			}
		}
	}
}

// sendWelcomeMessage 发送欢迎消息
func (p *SerialProxy) sendWelcomeMessage(session *serialSession) error {
	welcome := fmt.Sprintf(
		"\r\n=== Connected to %s serial console ===\r\n"+
			"Session ID: %s\r\n"+
			"Press Ctrl+] to disconnect\r\n\r\n",
		session.vmName,
		session.id,
	)

	if err := session.ws.SetWriteDeadline(time.Now().Add(5 * time.Second)); err != nil {
		session.logger.WithError(err).Warn("Failed to set write deadline")
	}
	return session.ws.WriteMessage(websocket.TextMessage, []byte(welcome))
}

// cleanup 清理会话资源
func (s *serialSession) cleanup() {
	s.cancel()
	close(s.stopCh)
	s.pingTicker.Stop()

	if s.stream != nil {
		if err := s.stream.Finish(); err != nil {
			s.logger.WithError(err).Warn("Failed to finish stream")
		}
		if err := s.stream.Free(); err != nil {
			s.logger.WithError(err).Warn("Failed to free stream")
		}
		s.stream = nil
	}

	if s.ws != nil {
		s.ws.Close()
		s.ws = nil
	}
}

// GetActiveSessions 获取活跃会话列表
func (p *SerialProxy) GetActiveSessions() []*SerialSessionInfo {
	p.sessionsMu.RLock()
	defer p.sessionsMu.RUnlock()

	sessions := make([]*SerialSessionInfo, 0, len(p.sessions))
	for _, s := range p.sessions {
		sessions = append(sessions, &SerialSessionInfo{
			ID:         s.id,
			VMName:     s.vmName,
			StartTime:  s.startTime,
			LastActive: s.lastActive,
			BytesRead:  s.bytesRead,
			BytesWrite: s.bytesWrite,
			Duration:   time.Since(s.startTime).String(),
		})
	}
	return sessions
}

// GetSessionInfo 获取单个会话信息
func (p *SerialProxy) GetSessionInfo(sessionID string) (*SerialSessionInfo, error) {
	p.sessionsMu.RLock()
	defer p.sessionsMu.RUnlock()

	session, exists := p.sessions[sessionID]
	if !exists {
		return nil, fmt.Errorf("session not found")
	}

	return &SerialSessionInfo{
		ID:         session.id,
		VMName:     session.vmName,
		StartTime:  session.startTime,
		LastActive: session.lastActive,
		BytesRead:  session.bytesRead,
		BytesWrite: session.bytesWrite,
		Duration:   time.Since(session.startTime).String(),
	}, nil
}

// CloseSession 关闭指定会话
func (p *SerialProxy) CloseSession(sessionID string) error {
	p.sessionsMu.RLock()
	session, exists := p.sessions[sessionID]
	p.sessionsMu.RUnlock()

	if !exists {
		return fmt.Errorf("session not found")
	}

	p.logger.WithField("session_id", sessionID).Info("Manually closing session")
	session.cancel()
	return nil
}

// CloseSessionsByVM 关闭指定虚拟机的所有会话
func (p *SerialProxy) CloseSessionsByVM(connID, vmName string) int {
	p.sessionsMu.RLock()
	sessions := make([]*serialSession, 0)
	for _, s := range p.sessions {
		if s.connID == connID && s.vmName == vmName {
			sessions = append(sessions, s)
		}
	}
	p.sessionsMu.RUnlock()

	for _, s := range sessions {
		s.cancel()
	}

	p.logger.WithFields(logrus.Fields{
		"vm":    vmName,
		"count": len(sessions),
	}).Info("Closed VM sessions")

	return len(sessions)
}

// === 令牌管理 ===

// generateToken 生成访问令牌
func (p *SerialProxy) generateToken(connID, vmName, consoleType string) string {
	tokenBytes := make([]byte, 32)
	if _, err := rand.Read(tokenBytes); err != nil {
		p.logger.WithError(err).Error("Failed to generate random token")
		// 如果随机数生成失败，使用时间戳作为后备方案
		tokenBytes = []byte(fmt.Sprintf("%s-%s-%d", connID, vmName, time.Now().UnixNano()))
	}
	token := hex.EncodeToString(tokenBytes)

	p.tokensMu.Lock()
	p.tokens[token] = &consoleToken{
		token:       token,
		connID:      connID,
		vmName:      vmName,
		consoleType: consoleType,
		createdAt:   time.Now(),
		expiresAt:   time.Now().Add(1 * time.Hour),
	}
	p.tokensMu.Unlock()

	// 启动令牌清理
	go p.cleanupExpiredToken(token)

	return token
}

// validateToken 验证令牌
func (p *SerialProxy) validateToken(token, connID, vmName, consoleType string) bool {
	p.tokensMu.RLock()
	defer p.tokensMu.RUnlock()

	t, exists := p.tokens[token]
	if !exists {
		p.logger.WithField("token", token[:8]+"...").Warn("Token not found")
		return false
	}

	if time.Now().After(t.expiresAt) {
		p.logger.WithField("token", token[:8]+"...").Warn("Token expired")
		return false
	}

	if t.connID != connID || t.vmName != vmName || t.consoleType != consoleType {
		p.logger.WithFields(logrus.Fields{
			"expected_conn": t.connID,
			"actual_conn":   connID,
			"expected_vm":   t.vmName,
			"actual_vm":     vmName,
		}).Warn("Token validation failed")
		return false
	}

	return true
}

// cleanupExpiredToken 清理过期令牌
func (p *SerialProxy) cleanupExpiredToken(token string) {
	time.Sleep(1*time.Hour + 5*time.Minute)

	p.tokensMu.Lock()
	delete(p.tokens, token)
	p.tokensMu.Unlock()

	p.logger.WithField("token", token[:8]+"...").Debug("Token cleaned up")
}

// RevokeToken 撤销令牌
func (p *SerialProxy) RevokeToken(token string) bool {
	p.tokensMu.Lock()
	defer p.tokensMu.Unlock()

	if _, exists := p.tokens[token]; exists {
		delete(p.tokens, token)
		p.logger.WithField("token", token[:8]+"...").Info("Token revoked")
		return true
	}
	return false
}

// GetTokenInfo 获取令牌信息
func (p *SerialProxy) GetTokenInfo(token string) (*consoleToken, error) {
	p.tokensMu.RLock()
	defer p.tokensMu.RUnlock()

	t, exists := p.tokens[token]
	if !exists {
		return nil, fmt.Errorf("token not found")
	}

	return t, nil
}

// generateSessionID 生成会话 ID
func (p *SerialProxy) generateSessionID(vmName string) string {
	return fmt.Sprintf("serial-%s-%d", vmName, time.Now().UnixNano())
}

// GetStats 获取代理统计信息
type ProxyStats struct {
	ActiveSessions  int    `json:"active_sessions"`
	TotalSessions   uint64 `json:"total_sessions"`
	ActiveTokens    int    `json:"active_tokens"`
	TotalBytesRead  uint64 `json:"total_bytes_read"`
	TotalBytesWrite uint64 `json:"total_bytes_write"`
	Uptime          string `json:"uptime"`
}

var (
	proxyStartTime  = time.Now()
	totalSessions   uint64
	totalBytesRead  uint64
	totalBytesWrite uint64
	statsMu         sync.RWMutex
)

func (p *SerialProxy) GetStats() *ProxyStats {
	p.sessionsMu.RLock()
	activeSessions := len(p.sessions)

	var bytesRead, bytesWrite uint64
	for _, s := range p.sessions {
		bytesRead += s.bytesRead
		bytesWrite += s.bytesWrite
	}
	p.sessionsMu.RUnlock()

	p.tokensMu.RLock()
	activeTokens := len(p.tokens)
	p.tokensMu.RUnlock()

	statsMu.RLock()
	defer statsMu.RUnlock()

	return &ProxyStats{
		ActiveSessions:  activeSessions,
		TotalSessions:   totalSessions,
		ActiveTokens:    activeTokens,
		TotalBytesRead:  totalBytesRead + bytesRead,
		TotalBytesWrite: totalBytesWrite + bytesWrite,
		Uptime:          time.Since(proxyStartTime).String(),
	}
}

func (p *SerialProxy) CloseAll() error {
	sessions := p.GetActiveSessions()
	for _, s := range sessions {
		if err := p.CloseSession(s.ID); err != nil {
			p.logger.WithError(err).Warn("Failed to close session")
		}
	}
	return nil
}
